package;
import fl.data.DataProvider;
using Chat;
class Chat extends flash.display.MovieClip
{
	var username : String;
	var logOutput : fl.controls.TextArea;
	var output : fl.controls.TextArea;
	var users : fl.controls.List;
	var input : fl.controls.TextInput;
	var btn : fl.controls.Button;
	var nc : flash.net.NetConnection;
	
	public function new(conn, name)
	{
		super();
		nc = conn;
		nc.addEventListener(flash.events.AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
		nc.client=this;
		username = name;
		createView();
	}
	//------- calls TO the server: ------------------------------------
	function onSendBtn(event:flash.events.MouseEvent):Void
	{
		nc.call("textInput", null, input.text);
		input.text="";
	}
	function updateUserList()
	{
		nc.call("getUsers", new flash.net.Responder(onResultGetUsers, onErrorGetUsers));
	}
	function onTextInput(event)
	{
		nc.call("isTyping", null);
	}
	
	//------- called FROM the server: ------------------------------------
	function textInput(date:Date, user, text:String):Void
	{
		output.text += formatDate(date) + " <" + user.name+"> "+text+"\n";
	}
	function userLeave(date, user)
	{
		output.text += formatDate(date) + " <" + user.name+"> left.\n";
		updateUserList();
	}
	function userJoin(date, user)
	{
		output.text += formatDate(date)+ " <" + user.name+"> joined.\n";
		updateUserList();
	}
	function isTyping(user)
	{
		addTypingUser(user);
	}
	
	//------- events (FROM) the server: -------------------------------
	function onNetStatus(event:flash.events.NetStatusEvent):Void
	{
		log("code:"+event.info.code);
		log("description:"+event.info.description);
	}
	function onAsyncError(event:flash.events.AsyncErrorEvent):Void
	{
		log("text:"+event.text);
		log("error:"+event.error);
	}
	function onResultGetUsers(data)
	{
		users.dataProvider = new DataProvider(data);
		users.labelField="name";
	}
	function onErrorGetUsers(obj)
	{
		log("Error:"+obj.toString());
	}
	
	// chat view logic
	var timeouts:Hash<UInt>;
	function addTypingUser(user)
	{
		if(timeouts==null)
			timeouts=new Hash();
		if(timeouts.exists(user.name))
			untyped __global__["flash.utils.clearTimeout"](timeouts.get(user.name));
		timeouts.remove(user.name);
		var index = getIndexFromUser(user);
		var indices = users.selectedIndices;
		while(indices.remove(index))	{}
		indices.push(index);
		users.selectedIndices = indices;
		timeouts.set(user.name, untyped __global__["flash.utils.setTimeout"](removeTypingUser,1000, user));
	}
	function removeTypingUser(user)
	{
		var index = getIndexFromUser(user);
		var indices = users.selectedIndices;
		while(indices.remove(index))	{}
		users.selectedIndices = indices;
	}
	function getIndexFromUser(user):Int
	{
		for(i in 0...users.dataProvider.length)
			if(users.dataProvider.getItemAt(i).name == user.name)
				return i;
		return -1;
	}
	function onUserChange(event)
	{
		log("user info: id:"+users.selectedItem.id+", name:"+users.selectedItem.name);
	}
	function log(msg:Dynamic)
	{
		logOutput.text+= formatDate(Date.now()) + " " + msg+"\n";
	}
	function formatDate(d:Date):String
	{
		var h:Dynamic = d.getHours();
		if(h<10) h="0"+h;
		var m:Dynamic = d.getMinutes();
		if(m<10) m="0"+m;
		return "[" + h + ":" + m + "]";
	}
	
	//view
	function createView()
	{
		var textFormat = new flash.text.TextFormat();
		textFormat.align = flash.text.TextFormatAlign.CENTER;
		
		var background = new flash.display.Shape();
		background.graphics.beginFill(0xDDDDFF, 1.0);
		background.graphics.drawRoundRect(0,0,1020, 660, 20, 20);
		addChild(background);

		logOutput = new fl.controls.TextArea();
		logOutput.restrict="";
		logOutput.tabChildren=false;
		logOutput.mouseChildren=false;
		logOutput.mouseEnabled=false;
		logOutput.x = 10;
		logOutput.y = 10;
		logOutput.width=1000;
		logOutput.height=100;
		addChild(logOutput);
		
		users = new fl.controls.List();
		users.x = logOutput.x;
		users.y = 10.under(logOutput);
		users.width = 200;
		users.height = 500;
		users.addEventListener(flash.events.Event.CHANGE, onUserChange);
		addChild(users);
			
		output = new fl.controls.TextArea();
		output.tabChildren=false;
		output.mouseChildren=false;
		output.mouseEnabled=false;
		output.x = 10.after(users);
		output.y = users.y;
		output.width = logOutput.width - output.x + 10;
		output.height = users.height;
		addChild(output);
		
		var label = new fl.controls.Label();
		label.text = username;
		label.setStyle("textFormat",textFormat);
		label.width = users.width;
		label.x = users.x;
		label.y = 10.under(users);
		addChild(label);
		
		btn = new fl.controls.Button();
		btn.label="send";
		btn.x = output.x + output.width - btn.width;
		btn.y= 10.under(output);
		btn.addEventListener(flash.events.MouseEvent.CLICK, onSendBtn);
		addChild(btn);
			
		input = new fl.controls.TextInput();
		input.x = 10.after(label);
		input.y = 10.under(output);
		input.width = btn.x - 10 - input.x;
		input.height = btn.height;
		input.addEventListener(flash.events.TextEvent.TEXT_INPUT, onTextInput);
		addChild(input);
	}
	public static function above(i:Int, t:Dynamic):Float
	{
		return cast t.y - t.height - i;
	}
	public static function under(i:Int, t:Dynamic):Float
	{
		return cast t.y + t.height + i;
	}
	public static function after(i:Int, t:Dynamic):Float
	{
		return cast t.x + t.width + i;
	}
	public static function before(i:Int, t:Dynamic):Float
	{
		return cast t.x - t.width - i;
	}
}
typedef User=
{
	var id:Int;
	var name:String;
}

